package pdh

import (
	"fmt"
	"runtime"
	"time"

	"gitee.com/general252/pdh/core"
)

type ObjectCounterValue struct {
	CounterName

	query    Query
	hCounter core.PDH_HCOUNTER

	Value     float64
	LastTime  time.Time
	TimeStamp int64
}

func newObjectCounterValue(query Query, name CounterName) *ObjectCounterValue {
	return &ObjectCounterValue{
		CounterName: name,
		query:       query,
	}
}

func (tis *ObjectCounterValue) getData() error {
	value, err := tis.query.GetFormattedCounterValueDouble(tis.hCounter)
	if err != nil {
		return err
	}

	tis.Value = value
	tis.LastTime = time.Now()
	tis.TimeStamp = tis.LastTime.Unix()
	return nil
}

func (tis *ObjectCounterValue) close() {
	_ = tis.query.RemoveCounter(tis.hCounter)
}

func (tis *ObjectCounterValue) ValueString() string {
	return tis.CounterName.String(tis.Value)
}

func (tis *ObjectCounterValue) GetCounter() uintptr {
	return uintptr(tis.hCounter)
}

type CounterName struct {
	Name  string
	Param string
	Type  CounterType
	Field CounterFieldType
}

func (tis *CounterName) String(value float64) string {
	switch tis.Type {
	case CounterTypeProcessor:
		switch tis.Field {
		case ProcessorUtility:
			return fmt.Sprintf("%.2f%%", value)
		}
	case CounterTypeMemory:
		switch tis.Field {
		case AvailableMBytes:
			return DataSize(int(value * 1024 * 1024))
		}
	case CounterTypeProcess:
		switch tis.Field {
		case ProcessorTime:
			cpuCount := runtime.NumCPU()
			return fmt.Sprintf("%.2f%%", value/float64(cpuCount))
		case HandleCount:
			return fmt.Sprintf("%v", int(value))
		case WorkingSetPrivate:
			return fmt.Sprintf("%v K", int(value/1024))
		}
	case CounterTypeNetworkInterface:
		switch tis.Field {
		case BytesReceivedPerSec:
			return fmt.Sprintf("%v/s", DataSize(int(value)))
		case BytesSentPerSec:
			return fmt.Sprintf("%v/s", DataSize(int(value)))
		case CurrentBandwidth:
			return fmt.Sprintf("%v Mbps", int(value)/1000/1000)
		}
	case CounterTypePhysicalDisk:
		switch tis.Field {
		case DiskTime:
			return fmt.Sprintf("%.2f%%", value)
		case AvgDiskBytesPerRead:
			return fmt.Sprintf("%v/s", DataSize(int(value)))
		case AvgDiskBytesPerWrite:
			return fmt.Sprintf("%v/s", DataSize(int(value)))
		case AvgDiskSecPerTransfer:
			return fmt.Sprintf("%v ms", time.Duration(value*float64(time.Second)).Milliseconds())
		case DiskTransfersPerSec:
			return fmt.Sprintf("%v/s", DataSize(int(value)))
		}
	}
	return fmt.Sprintf("%v", value)
}

type CounterNameBuilder struct {
	names []*CounterName
}

func NewCounterNameBuilder() *CounterNameBuilder {
	return &CounterNameBuilder{
		names: []*CounterName{},
	}
}

func (tis *CounterNameBuilder) Build() []*CounterName {
	return tis.names
}

func (tis *CounterNameBuilder) WithCPU() *CounterNameBuilder {
	param := Total
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeProcessor, param, ProcessorUtility)
	name := &CounterName{
		Name:  counterName,
		Param: param,
		Type:  CounterTypeProcessor,
		Field: ProcessorUtility,
	}
	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithAvailableMemory() *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v\\%v", CounterTypeMemory, AvailableMBytes)
	name := &CounterName{
		Name:  counterName,
		Param: "",
		Type:  CounterTypeMemory,
		Field: AvailableMBytes,
	}
	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithProcessCPU(processName string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeProcess, processName, ProcessorTime)
	name := &CounterName{
		Name:  counterName,
		Param: processName,
		Type:  CounterTypeProcess,
		Field: ProcessorTime,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithProcessHandleCount(processName string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeProcess, processName, HandleCount)
	name := &CounterName{
		Name:  counterName,
		Param: processName,
		Type:  CounterTypeProcess,
		Field: HandleCount,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) With(counterType CounterType, fieldType CounterFieldType, param string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeProcess, param, WorkingSetPrivate)
	name := &CounterName{
		Name:  counterName,
		Param: param,
		Type:  counterType,
		Field: fieldType,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithProcessMemory(processName string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeProcess, processName, WorkingSetPrivate)
	name := &CounterName{
		Name:  counterName,
		Param: processName,
		Type:  CounterTypeProcess,
		Field: WorkingSetPrivate,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithProcessElapsedTime(processName string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeProcess, processName, ElapsedTime)
	name := &CounterName{
		Name:  counterName,
		Param: processName,
		Type:  CounterTypeProcess,
		Field: ElapsedTime,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithProcessIDProcess(processName string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeProcess, processName, IDProcess)
	name := &CounterName{
		Name:  counterName,
		Param: processName,
		Type:  CounterTypeProcess,
		Field: IDProcess,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithProcessCreatingProcessID(processName string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeProcess, processName, CreatingProcessID)
	name := &CounterName{
		Name:  counterName,
		Param: processName,
		Type:  CounterTypeProcess,
		Field: CreatingProcessID,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithProcessIOReadBytesPerSec(processName string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeProcess, processName, IOReadBytesPerSec)
	name := &CounterName{
		Name:  counterName,
		Param: processName,
		Type:  CounterTypeProcess,
		Field: IOReadBytesPerSec,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithProcessIOWriteBytesPerSec(processName string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeProcess, processName, IOWriteBytesPerSec)
	name := &CounterName{
		Name:  counterName,
		Param: processName,
		Type:  CounterTypeProcess,
		Field: IOWriteBytesPerSec,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithProcessThreadCount(processName string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeProcess, processName, ThreadCount)
	name := &CounterName{
		Name:  counterName,
		Param: processName,
		Type:  CounterTypeProcess,
		Field: ThreadCount,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithNetworkInterfaceBytesReceivedPerSec(param string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeNetworkInterface, param, BytesReceivedPerSec)
	name := &CounterName{
		Name:  counterName,
		Param: param,
		Type:  CounterTypeNetworkInterface,
		Field: BytesReceivedPerSec,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithNetworkInterfaceBytesSentPerSec(param string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeNetworkInterface, param, BytesSentPerSec)
	name := &CounterName{
		Name:  counterName,
		Param: param,
		Type:  CounterTypeNetworkInterface,
		Field: BytesSentPerSec,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithNetworkInterfaceCurrentBandwidth(param string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypeNetworkInterface, param, CurrentBandwidth)
	name := &CounterName{
		Name:  counterName,
		Param: param,
		Type:  CounterTypeNetworkInterface,
		Field: CurrentBandwidth,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithPhysicalDiskDiskTime(param string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypePhysicalDisk, param, DiskTime)
	name := &CounterName{
		Name:  counterName,
		Param: param,
		Type:  CounterTypePhysicalDisk,
		Field: DiskTime,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithPhysicalDiskAvgDiskBytesPerRead(param string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypePhysicalDisk, param, AvgDiskBytesPerRead)
	name := &CounterName{
		Name:  counterName,
		Param: param,
		Type:  CounterTypePhysicalDisk,
		Field: AvgDiskBytesPerRead,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithPhysicalDiskAvgDiskBytesPerWrite(param string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypePhysicalDisk, param, AvgDiskBytesPerWrite)
	name := &CounterName{
		Name:  counterName,
		Param: param,
		Type:  CounterTypePhysicalDisk,
		Field: AvgDiskBytesPerWrite,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithPhysicalDiskAvgDiskSecPerTransfer(param string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypePhysicalDisk, param, AvgDiskSecPerTransfer)
	name := &CounterName{
		Name:  counterName,
		Param: param,
		Type:  CounterTypePhysicalDisk,
		Field: AvgDiskSecPerTransfer,
	}

	tis.names = append(tis.names, name)
	return tis
}

func (tis *CounterNameBuilder) WithPhysicalDiskDiskTransfersPerSec(param string) *CounterNameBuilder {
	counterName := fmt.Sprintf("\\%v(%v)\\%v", CounterTypePhysicalDisk, param, DiskTransfersPerSec)
	name := &CounterName{
		Name:  counterName,
		Param: param,
		Type:  CounterTypePhysicalDisk,
		Field: DiskTransfersPerSec,
	}

	tis.names = append(tis.names, name)
	return tis
}
